自己动手实现编译器理论篇(二) 语法解析(上) |
您所在的位置:网站首页 › vba 出现编译错误 语法错误怎么办 › 自己动手实现编译器理论篇(二) 语法解析(上) |
语法解析
语法解析的职责
一个语法解析器必须能够判断在给定编程语法的情况下,用户编写的程序在语法上是否合理。通常,编程语法需要用某种形式来描述,下面给出形式化的定义: Context-free grammar:CFG是用来描述如何构成句子的一系列规则的集合 Sentence:根据CFG生成的字符串 Production:CFG中的每条规则称为production Nonterminal symbol:production中的字符变量,能够被规则替换 Terminal symbol:句子中出现的单词,实际上是句法分析生成的Token Start symbol:grammer中的起始字符变量 描述表达式的语法例子 1 Expr -> Expr + Term 2 | Expr - Term 3 | Term 4 Term -> Term * Factor 5 | Term / Factor 6 | Factor 7 Factor -> ( Expr ) 8 | num 9 | name 复制代码Expr是我们的起始字符,Term、Factor是字符变量,num、name是我们的Token。对于每条规则,方便起见,顺序编了序号。现在看看我们根据定义的语法可以生成些什么?依次运用规则1->3->6->8->4->6->9->7->2->3->6->9->6->8,最终生成的表达式为4+a*(b-7)。这个过程可以使用语法生成树来表示: 解析与生成的过程相反:给定特定的sentence,我们需要构建出这棵语法生成树。根据构建过程可以将解析器分为两大类: Top-down parsers 从根节点出发,最终生长到叶子结点,在构建过程中,解析器会利用替换规则,将每个字符变量结点替换为一棵子树,直到所有叶子结点都不再可替换为止。 Bottom-up parsers 从叶子结点出发生长到根节点,在构建过程中,解析器寻找满足替换规则的父结点,随后将该结点加入树中。无论哪种解析方法,其中都涉及到替换规则的选择,不好的选择会对解析性能产生严重的影响,因此,合理高效的选择机制是解析算法的研究重点。 按照解析复杂度,我们可以把CFG(Context free grammar)分为4层: 任意CFG 没有限制条件的CFG,解析时间复杂度高达O(n^3) LR(1) 这类解析器使用自底向上算法,从左向右解析,基于当前字符,每次朝前看至多一个token。 LL(1) 该类解析器是LR(1)的子集,使用自顶向下算法,从左向右解析,每次朝前看至多一个token。 RG Regular grammar是一类特殊的CFG,替换规则只有两种:A→aA\rightarrow aA→a或者A→aBA\rightarrow aBA→aB,其中a为终止字符,A、B为字符变量。 几乎所有的编程语言结构都能够使用LR(1)形式表达,LL(1)形式更为常见。
上面给出了一种通用地从顶向下的左替解析算法,root表示根节点,focus表示当前替换规则中最左边的字符,算法使用数据栈存储替换规则中待匹配的字符,word表示当前的输入字符。首先进行初始化工作,接着进入一个大循环:如果当前字符是可替换地,那么选择一条规则替换当前字符,将规则中待匹配的字符逆序压入栈中,更新focus;如果focus不可替换且匹配到了word,此时将输入字符序列下一字符读取到word中,弹出栈顶元素,更新focus;如果所有输入字符均已匹配,返回解析树,否则进行回溯操作。 回溯操作通常自底向上逐层进行:如果当前所有替换规则均替换失败,回溯到上一层,重新进行替换,如果回溯到顶层依然匹配失败,此时返回语法错误提示。回溯实际上是对整个语法结构的遍历,这会耗费大量时间,如果有某种算法可以避免回溯,这将大大提高解析性能。 语法结构性问题将通用算法应用到我们本篇定义的表达式语法结构上,此时如果我们每次替换规则都选1,会发生什么事情呢?算法不会终止!这种现象称为Left-recursion。解决方法也很简单,我们可以重写语法替换规则,使得语法结构是Right-recursion即可: Expr -> Term Expr' Expr' -> + Term Expr' | - Term Expr' | \epsilon Term. -> Factor Term' Term' -> * Factor Term' | / Factor Term' | \epsilon 复制代码显式Left-recursion消除可以使用以下方法:
前面讲到,Top-down leftmost parser在解析时涉及回溯操作,这会降低解析性能。通过引入look-ahead技术,我们可以消解回溯,做到无回溯解析,对应地,这类语法叫做Backtrack-free grammar,也称作predictive grammar。在介绍无回溯解析算法前,先引入两个概念:First-set、Follow-set。 First-set 对于特定语法字符 α\alphaα,First(α)First(\alpha)First(α)是一个集合,包含所有从α\alphaα出发,利用语法替换规则生成的句子的首部终止字符。 Follow-set 对于给定的字符变量α\alphaα,Follow(α)Follow(\alpha)Follow(α)是一个集合,包含所有利用语法替换规则生成的句子中跟着α\alphaα立即出现的终止字符。First-set 具有如下性质: First(α)=α,if α∈{ϵ,eof,T}First(\alpha)=\alpha,\text{if }\alpha\in\left \{ \epsilon,eof,T \right \}First(α)=α,if α∈{ϵ,eof,T}下面给出一个计算First-set的算法:
我们试着给出前面表达式语法的First set: ExprExpr'TermTerm'FactorFirst(,name,num+,−,ϵ+,-,\epsilon+,−,ϵ(,name,num∗,/,ϵ*,/,\epsilon∗,/,ϵ(,name,num如果只使用First set,可能会出现一个问题:look-ahead字符不在First集合中怎么办,直接返回语法错误?这里的关键在于对ϵ\epsilonϵ的处理上,替换规则ϵ\epsilonϵ意思是跳过当前字符变量,转入其他替换规则中,但是从first-set的定义上可以看出,并不关心跳转操作,所以我们使用Follow-set来定义了跳转操作。下面给出计算Follow-set的算法:
结合First set与Follow set,我们给出对于规则的First set定义: First+(A→β)={First(β) if ϵ∉First(β)First(β)∪Follow(A) otherwise First^+(A\rightarrow \beta) = \begin{cases} First(\beta) &\text{ if } \epsilon\notin First(\beta) \\ First(\beta)\cup Follow(A) &\text{ otherwise } \end{cases}First+(A→β)={First(β)First(β)∪Follow(A) if ϵ∈/First(β) otherwise 对于任意的替换规则A→β1∣β2∣...∣βnA\rightarrow\beta_1|\beta_2|...|\beta_nA→β1∣β2∣...∣βn,如果存在如下条件: First+(A→βi) ∩ First+(A→βj)=∅, ∀ 1≤i,j≤n, i≠j.First^+(A\rightarrow \beta_i)\text{ } \cap \text{ }First^+(A\rightarrow \beta_j)=\varnothing,\text{ } \forall\text{ }1\leq i,j\leq n,\text{ }i\neq j.First+(A→βi) ∩ First+(A→βj)=∅, ∀ 1≤i,j≤n, i=j.我们就说具有该规则的语法是backtrack-free的。基于此,引出两种Top-down解析算法:Recursive-Descent算法、Table-Driven算法。 Recursive-Descent算法Recursive-Descent的想法是朴素的:对于每个字符变量S,根据给出的语法结构,将其写成对应的一个递归函数,这样以来,我们就把Backtrack-free grammar转换为多个互相调用的递归函数组合,下面给出一个实现例子: 序号ProductionFirst+First^+First+2Expr′→+ Term Expr′Expr'\rightarrow + \text{ }Term\text{ }Expr'Expr′→+ Term Expr′{+}\left \{ + \right \}{+}3∣− Term Expr′ \mid - \text{ }Term\text{ }Expr'∣− Term Expr′{−}\left \{ - \right \}{−}4∣ϵ\mid \epsilon∣ϵ{ϵ,eof,)}\left \{ \epsilon,eof,) \right \}{ϵ,eof,)} Eprime() /*Expr'-> + Term Expr' | - Term Expr'*/ if (word = + or word = -) then begin; word |
今日新闻 |
点击排行 |
|
推荐新闻 |
图片新闻 |
|
专题文章 |
CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭 |